package com.basker.pisces.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.springframework.util.Assert;

import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.exception.SerializeRuntimeException;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.io.UnsafeSerializer;

/**
 * 序列化工具类
 *
 * @author hangwen
 */
public class SerializeUtils {

    /**
     * 使用hessian2协议，将二进制数据反序列化成对象
     *
     * <p>
     * obj参数可以不用实现{@link Serializable},所以默认关闭{@link SerializerFactory#isAllowNonSerializable()}参数，
     * 这样默认使用{@link UnsafeSerializer}
     *
     * @param obj
     * @return
     */
    public static Object deserialize(byte[] data) {
        return deserialize(data, SerializeType.Hession2);
    }

    /**
     * 指定序列化方式，将二进制数据反序列化成对象
     *
     * @param obj
     * @param serializeType 如果是{@link SerializeType#JDK}则需要对象需要实现{@link Serializable}接口，
     *                      {@link SerializeType#Hession2}可以不做该要求
     * @return
     */
    public static Object deserialize(byte[] data, SerializeType serializeType) {
        Assert.notNull(data, "parameter 'data' is required");

        try {
            switch (serializeType) {
                case JDK:
                    return deserByJDK(data);
                case Hession2:
                default:
                    return deserByHession2(data);
            }
        } catch (IOException | ClassNotFoundException e) {
            throw ExceptionFactory.createRuntimeException(SerializeRuntimeException.class, e,
                    "pisces-common.SerializeUtil.deserialize-001", serializeType, e.getMessage());
        }
    }

    /**
     * 使用hessian2协议，将对象序列化成二进制数据。
     *
     * <p>
     * obj参数可以不用实现{@link Serializable},所以默认关闭{@link SerializerFactory#isAllowNonSerializable()}参数，
     * 这样默认使用{@link UnsafeSerializer}
     *
     * @param obj
     * @return
     */
    public static byte[] serialize(Object obj) {
        return serialize(obj, SerializeType.Hession2);
    }

    /**
     * 指定序列化方式，将对象序列化成二进制数据。
     *
     * @param obj
     * @param serializeType 如果是{@link SerializeType#JDK}则需要对象需要实现{@link Serializable}接口，
     *                      {@link SerializeType#Hession2}可以不做该要求
     * @return
     */
    public static byte[] serialize(Object obj, SerializeType serializeType) {
        Assert.notNull(obj, "parameter 'obj' is required");

        try {
            switch (serializeType) {
                case JDK:
                    return serByJDK(obj);
                case Hession2:
                default:
                    return serByHession2(obj);
            }
        } catch (IOException e) {
            throw ExceptionFactory.createRuntimeException(SerializeRuntimeException.class, e,
                    "pisces-common.SerializeUtil.serialize-001", serializeType, e.getMessage());
        }
    }

    private static Object deserByHession2(byte[] data) throws IOException {
        SerializerFactory serializerFactory = SerializerFactory.createDefault();
        serializerFactory.setAllowNonSerializable(true);

        Hessian2Input hi = null;

        try {
            ByteArrayInputStream is = new ByteArrayInputStream(data);
            hi = new Hessian2Input(is);
            hi.setSerializerFactory(serializerFactory);

            Object obj = hi.readObject();
            return obj;
        } finally {
            if (hi != null) {
                hi.close();
            }
        }
    }

    private static Object deserByJDK(byte[] data) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = null;

        try {
            ByteArrayInputStream is = new ByteArrayInputStream(data);
            ois = new ObjectInputStream(is);

            Object obj = ois.readObject();
            return obj;
        } finally {
            if (ois != null) {
                ois.close();
            }
        }
    }

    private static byte[] serByHession2(Object obj) throws IOException {
        SerializerFactory serializerFactory = SerializerFactory.createDefault();
        serializerFactory.setAllowNonSerializable(true);

        Hessian2Output ho = null;

        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ho = new Hessian2Output(os);
            ho.setSerializerFactory(serializerFactory);

            ho.writeObject(obj);
            ho.flush();

            return os.toByteArray();
        } finally {
            if (ho != null) {
                ho.close();
            }
        }
    }

    private static byte[] serByJDK(Object obj) throws IOException {
        ObjectOutputStream oos = null;

        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(os);

            oos.writeObject(obj);
            oos.flush();

            return os.toByteArray();
        } finally {
            if (oos != null) {
                oos.close();
            }
        }
    }

    /**
     * 序列化类型枚举类
     *
     * @author hangwen
     */
    public static enum SerializeType {
        /**
         * java默认的序列化
         */
        JDK,

        /**
         * hession2序列化协议
         */
        Hession2
    }

}
